<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/7/1 0001
 * Time: 8:31
 */

namespace api\storage\service;

use api\storage\model\FeedStationModel;
use api\storage\model\ReelsModel;
use api\storage\model\StorageModel;
use service\file\CommonFile;

use api\storage\model\FeedDetailModel;
use api\storage\model\SmtMaterielModel;
use service\logger\LoggerFactory;
use think\Db;
use think\Request;

use api\storage\common\Constant;

class FeedService extends BaseService
{
    protected $feedDetailModel;
    protected $data;
    protected $storageModel;
    protected $file;
    protected $storageUuid;
    protected $feedStationModel;
    protected $smtMaterielModel;
    protected $filePath;
    protected $logger;
    protected $reelsModel;
    /*****
     * FeedService constructor.
     * 这个文件主要用于出库前的流程准备，比如进行模拟齐套，还有就是对模拟数量的校验与加减等。
     * 出库部分写在另一个文件：FeedOutputServer中
     *
     */
    public function __construct()
    {
        $this->feedDetailModel = new FeedDetailModel();
        $this->storageModel = new StorageModel();
        $this->feedStationModel = new FeedStationModel();
        $this->smtMaterielModel = new SmtMaterielModel();
        $this->reelsModel = new ReelsModel();
        $this->logger = LoggerFactory::getLogger('feed');
    }

    /****
     * @param $feedId
     * @param string $storageUuid
     * @return array|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException 根据feed_id 检查是否齐套，已经是否有错误
     * @throws \think\Exception
     */
    public function checkSimulation($feedId ,$storageUuid = "184928afb9e35f3ff421da05fc3f917f" )
    {
        $total = $this->feedDetailModel->getTotalAndSimulation(['feed_id' => $feedId])->toArray();//获取所需的物料Id以及他们的数量，还有对应可用的模拟量

        $nullMateriel = $this->checkSmtMateriel($total);//确认Smt仓库是否存在料站表所需的物料，若不存在则将缺少的物料Id赋值给$nullMateriel
        if (!empty($nullMateriel)) {//如果不缺所需物料的话，值为NULL
            return $nullMateriel;//如果缺少的话，进入状态3，返回缺少的物料Id
        };

        $nullMateriel = $this->checkFeedMateriel($total);//确认料站细表的物料Id是否为空
        if (!empty($nullMateriel)) {//若不为空则返回Null
            return $nullMateriel;
        };

        $flag = 1;//初始化flag标志，用于确认是否出现不齐套物料

        foreach ($total as $item) {//遍历每一个物料
            if ($item['total_count'] > $item['simulation_num']) {//如果所需物料大于可用物料
                $flag = 0;//将flag设置为不齐套
                $data[] = [
                    'subMaterielId' => $item['sub_materiel_id'],
                    'simulationNum' => $item['simulation_num'],
                    'totalCount' => $item['total_count'],
                    'lackCount' => $item['total_count'] - $item['simulation_num']
                ];//插入不符合的物料的信息
            }
        }

        if ($flag == 0) {//如果为flag为0，说明不齐套
            return $this->resultTemplate(1, $data);
        } else {//排除所有错误情况，剩下齐套。

            $result = $this->checkReelPlants($feedId);//检查在库物料盘数是否足够出首盘
            if ($result == true){
                return $result;//如果不够的话，result为真，返回不足的数据数据
            }

            foreach ($total as $item) {
                $materielTime[] = [
                    'subMaterielId' => $item['sub_materiel_id'],
                    'updateTime' => $item['update_time']
                ];
            }

            $this->checkFile($feedId, $storageUuid);
            $this->file->write(json_encode($materielTime));


            $this->logger->info("工单:{$feedId}齐套,更新模拟数量");
            return $this->resultTemplate(4, $materielTime);
        }
    }

    /****
     * @param $total
     * @return array|null
     * 确认料站细表的物料Id是否为空
     */
    public function checkFeedMateriel($total)
    {
        $flag = 1;
        foreach ($total as $item) {
            if (empty($item['simulation_num'])) {
                $flag = 0;
                $data[] = [
                    'subMaterielId' => $item['sub_materiel_id'],
                ];
            }
        }
        if ($flag == 0) {
            return $this->resultTemplate(3, $data);
        } else {
            return null;
        }
    }

    /***
     * @param $total
     * @return array|null
     * /确认Smt仓库是否存在料站表所需的物料，若不存在则将缺少的物料Id赋值给$nullMateriel
     */

    public function checkSmtMateriel($total)
    {
        $flag = 1;
        foreach ($total as $item) {
            if (empty($item['sub_materiel_id'])) {
                $flag = 0;
            }
        }
        if ($flag == 0) {
            return $this->resultTemplate(2);
        } else {
            return null;
        }
    }

    /****
     * @param $feedId
     * @param string $storageUuid
     * @return array
     * @throws \think\Exception
     * 确认工单此时的状态：
     * 工单号不存在
     * 工单正在生产
     * 工单已完成
     * 已经有人正在出库
     */
    public function getFeedData($feedId, $storageUuid = '184928afb9e35f3ff421da05fc3f917f')//目前只有smt库用到了这个，所以这边先这么写，之后要扩展再改
    {


        $result = $this->feedStationModel->checkStatus(['feed_id' => $feedId]);
        if (empty($result)) {
            return $this->resultTemplate(3);//工单号不存在
        }
        $result = $result->toArray();
        if ($result['status'] == 1) {

            return $this->resultTemplate(1);//工单正在生产
        }
        if ($result['status'] == 2) {
            return $this->resultTemplate(2);//工单已完成
        }

        $this->outPutFile($feedId, $storageUuid);//获取准备出库地址

        $data = $this->jsonDecode($this->file->read());

        if ($this->isNotEmpty($data)) {
            //已经存在文件，已经有人正在出库了
            return $this->resultTemplate(0, $data);
        }
        $this->logger->info("工单{$feedId}进入齐套检查");
        return $this->resultTemplate(4);
    }

    /***
     * @param $feedId
     * @param string $storageUuid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @throws \Exception
     * 更新Simulation（可用数量，模拟量）
     *
     * 在更新前，要先确认数据有没有发生变动，
     * 否则有可能上一个流程齐套齐套，但是其他PDA并发更新了数量
     * 导致运行到这个流程时已经已经不齐套的情况发生
     *
     */
    public function updateSimulation($feedId, $storageUuid = "184928afb9e35f3ff421da05fc3f917f")
    {
        $this->checkFile($feedId, $storageUuid);//feedCheck文件

        $data = json_decode($this->file->read(), true);//解析为数组文件

        $checkTimes = array_column($data, 'updateTime', "subMaterielId");//解析数组，将里面的数据变成 $checkTimes['subMaterielId'] = 'updateTime'(之前模拟出库的检验位)

        $total = $this->feedDetailModel->getTotalAndSimulation(['feed_id' => $feedId])->toArray();//获取是smt_materiel的最后更新时间，用来校对
        $this->smtMaterielModel->startTrans();//开启事务
        foreach ($total as $item) {
            $simulation_num = $item['simulation_num'] - $item['total_count'];//算出校对后的数量
            $this->smtMaterielModel->updateSimulation(['simulation_num' => $simulation_num], ['sub_materiel_id' => $item['sub_materiel_id']]);//更新数量
            $this->smtMaterielModel->updateCheckTime(['sub_materiel_id' => $item['sub_materiel_id']]);
            if ($item['update_time'] != $checkTimes[$item['sub_materiel_id']]) {//判断当前的smt_materiel最后更新时间是否等于之前进行模拟时的更新时间，
                $this->smtMaterielModel->rollback();//不等于说明模拟值（可用数量）发生了变化，触发了数据库的更新，所以回滚
                $this->logger->warn("模拟数量发生改变，工单{$feedId}重新开始模拟。");
                return $this->resultTemplate(1);
            }
        }
        $this->smtMaterielModel->commit();//提示事务更新。

        $this->outPutFile($feedId, $storageUuid);//这边是对出库文件进行一个初始化
        $where['feed_id'] = $feedId;
        $field = 'feed_id,sub_materiel_id,total_count,sent_count,waste,machine_id,seat_code,status';
        $temp = $this->feedDetailModel->getFeedDetail($where, $field)->toArray();//寻找符合条件的数据
        $result = $this->feedStationTemplate($temp);
        $this->file->write(json_encode($result));//将格式写入到程序中
        $this->logger->info("工单{$feedId}更新模拟量成功");
        return $this->resultTemplate(2,$result);

    }

    /***
     * @param $feedId
     * @param $storageUuid
     * 初始化feed出库文件
     */
    protected function outPutFile($feedId, $storageUuid)
    {
        $this->storageUuid = $storageUuid;
        $this->filePath = Constant::RECORD_FEED_PREPARE_POSITION .$feedId . Constant::RECORD_FILE_SUFFIX;
        $this->file = new CommonFile($storageUuid, $this->filePath);

    }


    /***
     * @param $feedId
     * @param $storageUuid
     * 用于防止并发导致的simulation错误，而用来存放乐观锁检查字段的文件
     */
    protected function checkFile($feedId, $storageUuid)
    {
        $this->storageUuid = $storageUuid;
        $filePath = Constant::RECORD_FEED_CHECK . $feedId . Constant::RECORD_FILE_SUFFIX;
        $this->file = new CommonFile($storageUuid, $filePath);

    }

    /***
     * @param $temp
     * @return mixed
     * 料站表模板，转换数组格式，之后批量插入检查文件中。
     */
    public function feedStationTemplate($temp){
        foreach ($temp as $item){

            $data['feedStationData'][] = [
                "materiel" => $item['sub_materiel_id'],
                "totalCount" => $item['total_count'],
                "sentCount" => $item['sent_count'],
                "waste" => $item['waste'],
                "machineId" => $item['machine_id'],
                "seatCode" => $item['seat_code'],
                "status" => $item['status'],
                "isFinish" => false,
                "isFirst" => true,
                "machineSeat" => $item['machine_id']."#".$item['seat_code'],
            ];

        }
        $data['reelsData'] = [];
        $data['process'] = Constant::FEED_OUTPUT_PROCESS_MOVE_MATERIEL;
        return $data;

    }

    /**
     * @param $feedId
     * @return array
     * @throws \think\Exception
     * 检查库中盘料是否足够出首盘，如果不足的话返回不足的数量
     * 如果足够的话返回false
     */
    public function checkReelPlants($feedId){
        $flag = 1;
        $feedWhere['feed_id'] = $feedId;
        $feedDetailMateriel = $this->feedDetailModel->getMaterielNumber($feedWhere)->toArray();
        $feedMaterielNum = array_column($feedDetailMateriel,'count','sub_materiel_id');
        foreach ($feedDetailMateriel as $item){
            $reelsWhere[] = $item['sub_materiel_id'];
        }
        $reelsMaterielObj = $this->reelsModel->getMaterielNumber(array_unique($reelsWhere));

        $reelsMateriel = $reelsMaterielObj->toArray();
        $reelsMaterielNum = array_column($reelsMateriel,'count','sub_materiel_id');

        foreach ($feedMaterielNum as $key => $item){
            $number = $feedMaterielNum[$key] - $reelsMaterielNum[$key];//所需该类物料的盘料数量 - 库中存在的盘料数量
            if($number > 0){//如果大于0，说明存在盘料不足的现象
                $flag = 0;
                $needNumber[$key] = $number;//将缺了多少盘料直接放入到这里
                $lackInfo[] = [
                    'materielId' => $key,
                   'lackNum' => $number,
                ];
            }
        }
        if($flag == 1){
            return  false;
        }
        return $this->resultTemplate(5,$lackInfo);
    }






}